using StackExchange.Redis;

namespace oms.Shared.Services.Redis;

/// <summary>
/// Redis服务实现类
/// </summary>
public class RedisService : IRedisService
{
    private readonly IDatabase _redisDb;
    private readonly IConnectionMultiplexer _connectionMultiplexer;

    // Lua 脚本：自增 + 首次设置过期时间（过期时间由外部传入）
    private static readonly RedisValue LuaScript = (RedisValue)@"
        local current = redis.call('INCR', KEYS[1])
        if current == 1 then  -- 首次创建键时设置过期时间
            redis.call('EXPIRE', KEYS[1], ARGV[1])
        end
        return current
    ";

    public RedisService(IConnectionMultiplexer redisMultiplexer)
    {
        _connectionMultiplexer = redisMultiplexer;
        _redisDb = _connectionMultiplexer.GetDatabase();
    }

    /// <summary>
    /// 获取下一个序列值（每日过期）
    /// </summary>
    /// <param name="key">键名</param>
    /// <returns>下一个序列值</returns>
    public async Task<long> GetNextSequenceWithDailyExpiryAsync(string key)
    {
        // 动态计算过期时间（到次日 00:05 的秒数）
        var expiry = GetExpiryToNextDay0005();
        var expirySeconds = (long)expiry.TotalSeconds;

        // 执行 Lua 脚本
        var result = await _redisDb.ScriptEvaluateAsync(
            script: LuaScript!,
            [key],
            [expirySeconds] // 传入动态计算的过期秒数
        );

        return (long)result;
    }

    /// <summary>
    /// 从Redis中获取值
    /// </summary>
    /// <param name="key">键</param>
    /// <returns>值</returns>
    public async Task<string?> GetAsync(string key)
    {
        return await _redisDb.StringGetAsync(key);
    }

    /// <summary>
    /// 计算到次日 00:05 的时间间隔
    /// </summary>
    /// <returns></returns>
    private static TimeSpan GetExpiryToNextDay0005()
    {
        var now = DateTime.Now;
        // 次日 00:05 的时间
        var targetTime = new DateTime(
            now.Year,
            now.Month,
            now.Day,
            0, 5, 0 // 00:05:00
        ).AddDays(1); // 加 1 天，得到次日 00:05

        // 计算间隔（若为负数，说明当前时间已过今日 00:05，需加 1 天）
        var interval = targetTime - now;
        return interval.TotalSeconds < 0 ? interval.Add(TimeSpan.FromDays(1)) : interval;
    }


    /// <summary>
    /// 测试Redis连接
    /// </summary>
    /// <returns>是否连接成功</returns>
    public async Task<bool> TestConnectionAsync()
    {
        try
        {
            if (!_connectionMultiplexer.IsConnected)
            {
                await _redisDb.PingAsync();
            }

            var testKey = $"redis:test:{Guid.NewGuid():N}"; // 唯一键名，避免冲突
            const string testValue = "connection_test_value";
            var expiry = TimeSpan.FromSeconds(30); // 30秒后自动过期，避免残留

            // 写入测试
            var setSuccess = await _redisDb.StringSetAsync(testKey, testValue, expiry);
            if (!setSuccess)
            {
                throw new Exception("写入测试数据失败");
            }

            // 读取测试
            var getValue = await _redisDb.StringGetAsync(testKey);
            return getValue == testValue;
        }
        catch
        {
            return false;
        }
    }
}